// Tierless
// Copyright © 2010 Alphora
// This file is licensed under a modified BSD-license (see tierless_license.txt).
using System;
using System.IO;

namespace Tierless.Infrastructure
{
	public delegate void ExecuteDelegate();
		
	public class LoggingManager : IDisposable
	{
		public const int CMaxLogs = 10;
		
		public const string CDefaultLogName = "App";
		
		private static LoggingManager FDefault;
		public static LoggingManager Default
		{
			get
			{
				if (FDefault == null)
				{
					FDefault = new LoggingManager();
					FDefault.Start();
				}
				return FDefault;
			}
		}
		
		public static bool LoggedExecute(ExecuteDelegate AMethod)
		{
			try
			{
				Default.Log(String.Format("Executing {0}.", AMethod.Method.Name));
				AMethod();
				Default.Log(String.Format("Execution completed."));
				return true;
			}
			catch (Exception LException)
			{
				Default.LogError(LException);
				return false;
			}
		}

		public LoggingManager() : this(CDefaultLogName) { }

		public LoggingManager(string ALogName)
		{
			LogName = ALogName;
			LogFileName = GetLogFileName();
		}
		
		public void Dispose()
		{
			Stop();
		}

		public string LogName { get; private set; }

		public string LogFileName { get; private set; }

		private FileStream FLogFile;
		
		private StreamWriter FLog;
		
		private object FLogSync = new object();
		
		private bool FLogging;
		public bool Logging
		{
			get
			{
				return FLogging;
			}
			set
			{
				if (FLogging != value)
				{
					if (value)
						Stop();
					else
						Start();
				}
			}
		}
		
		private int FPauseCount;
		public bool IsPaused
		{
			get { return FPauseCount > 0; }
			set
			{
				if (value)
					Pause();
				else
					Resume();
			}
		}
		
		protected virtual string GetLogFileName()
		{
			var LLogFileName = GetLogFileName(CMaxLogs);
			if (File.Exists(LLogFileName))
				File.Delete(LLogFileName);
				
			for (var LIndex = CMaxLogs - 1; LIndex >= 0; LIndex--)
			{
				LLogFileName = GetLogFileName(LIndex);
				if (File.Exists(LLogFileName))
					File.Move(LLogFileName, GetLogFileName(LIndex + 1));
			}
			
			return GetLogFileName(0);
		}

		protected virtual string GetLogFileName(int ALogIndex)
		{
			return 
				Path.Combine
				(
					AppDomain.CurrentDomain.BaseDirectory, 
					String.Format
					(
						"{0}{1}.log", 
						LogName, 
						ALogIndex == 0 ? String.Empty : ALogIndex.ToString()
					)
				);
		}
		
		public void Start()
		{
			lock (FLogSync)
			{
				if (FLogFile == null)
				{
					FLogFile = new FileStream(LogFileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read);
					FLogFile.Position = FLogFile.Length;
					FLog = new StreamWriter(FLogFile) { AutoFlush = true };

					FLogging = true;
					FPauseCount = 0;
				}
			}
		}

		public void Stop()
		{
			lock (FLogSync)
			{
				FLogging = false;

				if (FLog != null)
				{
					FLog.Flush();
					FLog.Close();
					FLog = null;
				}
				
				if (FLogFile != null)
				{
					FLogFile.Close();
					FLogFile = null;
				}
			}
		}

		public void Pause()
		{
			lock (FLogSync)
			{
				FPauseCount++;
			}
		}
		
		public void Resume()
		{
			lock (FLogSync)
			{
				FPauseCount--;
			}
		}
		
		public void Log(string AMessage)
		{
			if (!FLogging)
				throw new InvalidOperationException("The log must be started before messages can be logged.");
				
			if (FPauseCount == 0)
			{
				lock (FLogSync)
				{
					FLog.Write(String.Format("{0:yyyy/MM/dd HH:mm:ss.ffff} {1}\r\n", DateTime.Now, AMessage));
				}
			}
		}
		
		public void LogError(Exception AException)
		{
			Log(AException.ToString());
		}
	}
}
